#!/usr/bin/env python
import os
import subprocess
import mysql.connector
import logging
from datetime import datetime
import re
import requests
import json
import ConfigParser

config = ConfigParser.ConfigParser()
config.read('/etc/fileserver/config.ini')

class FileStatus:
    NOT_SCANNED = 0
    SCANNING = 1
    NORMAL = 2
    EXCEPTION = 3
    UNKNOWN = 4

MYSQL_CONFIG = {
    'host': config.get("mysql","host"),
    'user': config.get("mysql","user"),
    'password': config.get("mysql","password"),
    'database': config.get("mysql","database"),
    'charset': config.get("mysql","charset"),
    'port'   : config.get("mysql","port")
}

LOG_FILE_PATH = '/root/file_manager_python/log/security.log'
logging.basicConfig(filename=LOG_FILE_PATH, level=logging.INFO)
logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s')

def update_file_status(uuid, new_status_value):
#    url_template = "http://admin.rbi.afafa.xyz/manager/server/v1.0/file_record/{uuid}/"
   # url_template = "http://manager/server/v1.0/file_record/{uuid}/"
    url = config.get("sys","file_url")+"{uuid}/"
    url = url_template.format(uuid=uuid)

    payload = {"status": new_status_value}
    logging.info("update file status  %s" % (new_status_value))
    try:
        headers = {'Content-Type': 'application/json'}
        response = requests.patch(url, data=json.dumps(payload), headers=headers)
        if response.status_code == 200:
            response_data = response.json()

        else:
            logging.info("update file status error  %s" % (response.status_code))
            #print("error{}".format(response.status_code))
            #return None

    except requests.exceptions.RequestException as e:
        logging.info("update file status  %s" % (response.status_code))
        #print("info {}".format(e))
        #return None

def parse_clamscan_output(output):
    result = {}

    pattern = r'(\S+?):\s*(.+?(?=\n))'
    matches = re.findall(pattern, output, re.MULTILINE | re.DOTALL)

    for match in matches:
        file_path, scan_result = match
        result[file_path] = scan_result.strip()

    return result    

#def parse_clamscan_output(output):
#    result = {}
#    logging.info("IIIIIIIIIIII parse_clamsan output  %s" % (output)) 
#    pattern = r'(\/[\w\.-]+)+:\s*(OK|FOUND)'
#    matches = re.findall(pattern, output, re.MULTILINE)
#
#    for match in matches:
#        file_path, scan_result = match
#        result[file_path] = scan_result
#
#    return result

def extract_infected_files(output):
    summary_start = "----------- SCAN SUMMARY -----------"
    detail_pattern = r'([\w\/\.]+):\s*(FOUND)'
    logging.info("IIIIIIIIIIIIn extract infectex files  %s" % (output))
    detail_lines = output.decode('utf-8').splitlines()
    for index, line in enumerate(detail_lines):
        if line.startswith(summary_start):
            details_index = index + 2
            break

    infected_files = {}
    for line in detail_lines[details_index:]:
        match = re.match(detail_pattern, line)
        if match:
            file_path, _ = match.groups()
            infected_files[file_path] = "FOUND"

    return infected_files


def scan_file_and_update_db(uuid, file_name):
    file_to_scan = os.path.join('/var/www/html/', file_name)
    logging.info("Starting clamscan at %s for file: %s" % (datetime.now(), file_name))

    try:
        output = subprocess.check_output(['clamscan', file_to_scan], stderr=subprocess.STDOUT)
        clamscan_exit_code = subprocess.call(['clamscan', file_to_scan])
        if clamscan_exit_code == 0:
            desired_status = FileStatus.NORMAL
            virus_details = "OK"
        elif clamscan_exit_code == 1:
            parsed_results = extract_infected_files(output.decode('utf-8'))
            #parsed_results = parse_clamscan_output(output.decode('utf-8'))
            if file_name in parsed_results:
                desired_status = FileStatus.EXCEPTION
                virus_details = parsed_results[file_name]
            else:
                desired_status = FileStatus.UNKNOWN
                virus_details = "File flagged by clamscan but not found in detailed output"
        else:
            desired_status = FileStatus.UNKONWN
            virus_details = "Unexpected clamscan exit code: %d" % clamscan_exit_code
    except subprocess.CalledProcessError as e:
        logging.error("Unexpected clamscan error with file %s. Exit code: %s, Output: %s" % (file_name, e.returncode, e.output.decode('utf-8')))
        #parsed_results = extract_infected_files(e.output.decode('utf-8'))
        parsed_results = parse_clamscan_output(e.output.decode('utf-8'))
        logging.info("parsed_results %s file to scan is %s" % (parsed_results, file_to_scan))
        if file_to_scan in parsed_results:
            desired_status = FileStatus.EXCEPTION
            virus_details = parsed_results[file_to_scan]
        else:
            desired_status = FileStatus.UNKOWN
            virus_details = "File flagged by clamscan but not found in detailed output"
        #desired_status = FileStatus.EXCEPTION
        #virus_details = "Clamscan execution failure"

    update_mysql_status(uuid, file_name, desired_status, virus_details=virus_details)
    update_file_status(uuid, desired_status)

def update_mysql_status(uuid, file_name, new_status_value, virus_details=None):
    cnx = mysql.connector.connect(**MYSQL_CONFIG)
    cursor = cnx.cursor()

    logging.info("update mysql status virus_details %s" % virus_details)
    query = (
        "INSERT INTO filesecurity (file_uuid, file_name, status, virus_details) "
        "VALUES (%s, %s, %s, %s) "
        "ON DUPLICATE KEY UPDATE status = VALUES(status), virus_details = VALUES(virus_details)"
    )
    data = (uuid, file_name, new_status_value, virus_details or '')

    cursor.execute(query, data)
    cnx.commit()

    cursor.close()
    cnx.close()

def periodic_scan_and_update():
    cnx = mysql.connector.connect(**MYSQL_CONFIG)
    cursor = cnx.cursor()

    query = ("SELECT file_uuid, file_name FROM filesecurity WHERE status = %s" % FileStatus.NOT_SCANNED)
    cursor.execute(query)

    for (uuid, file_name) in cursor.fetchall():
        scan_file_and_update_db(uuid, file_name)
    logging.info("Finished periodic scanning at %s" % datetime.now())
    cursor.close()
    cnx.close()

import time
while True:
    periodic_scan_and_update()
    time.sleep(5)
